//
//  MFWClick.m
//  MFWClick framework
//
//  Created by hanxin on 11/6/12.
//  Copyright (c) 2012 mafengwo. All rights reserved.
//

#import "MFWClick.h"

#import "OpenUDID.h"

#include <sys/socket.h>
#include <sys/sysctl.h>
#include <net/if.h>
#include <net/if_dl.h>
#include <AdSupport/AdSupport.h>
#import <zlib.h>


#define KeyTriggerPage          @"_tp"
#define KeyTriggerPoint         @"_tpt"
#define KeyTriggerLevel         @"_tl"
#define KeyTriggerPath          @"_tpa"

#define KeyTriggerGuid          @"_tid"
#define KeyTriggerObjectType    @"_tot"
#define KeyTriggerObjectId      @"_toid"
#define KeyTriggerPrevious      @"_tpre"

@interface MFWClickTrigger()
{
    NSString* _guid;
}

@property (nonatomic,strong) MFWClickTrigger* preTrigger;

@property (nonatomic,strong) NSString* guid;
@property (nonatomic,strong) NSString* page;
@property (nonatomic,strong) NSString* point;
@property (nonatomic,strong) NSString* objectType;
@property (nonatomic,strong) NSString* objectId;

@property (nonatomic,readonly) NSInteger level;

@end

@implementation MFWClickTrigger

/** 触发点生成 */
+(instancetype)triggerWithPage:(NSString*)page
                         point:(NSString*)point
                        object:(id)object
                   prevTrigger:(MFWClickTrigger*)preTrigger
{
    NSString* objectType = nil;
    NSString* objectId = nil;
    if (object) {
        objectType = NSStringFromClass([object class]);
        if ([object respondsToSelector:@selector(id)]) {
            id idValue = [object performSelector:@selector(id)];
            if ([idValue isKindOfClass:[NSString class]]) {
                objectId = idValue;
            }
        }
    } 

    MFWClickTrigger* trigger = [MFWClickTrigger triggerWithPage:page point:point objectType:objectType objectId:objectId prevTrigger:preTrigger];
    
    return trigger;
}

/** 触发点生成 */
+(instancetype)triggerWithPage:(NSString*)page
                         point:(NSString*)point
                    objectType:(NSString*)objectType
                      objectId:(NSString*)objectId
                   prevTrigger:(MFWClickTrigger*)preTrigger
{
    MFWClickTrigger* trigger = [[MFWClickTrigger alloc] init];
    trigger.page = page;
    trigger.point = point;
    trigger.objectType = objectType;
    trigger.objectId = objectId;
    trigger.preTrigger = preTrigger;
    
    return trigger;
}

- (id)init
{
    self = [super init];
    if (self) {
        self.guid = [MFWClick GetUUID];
    }
    return self;
}

- (NSInteger)level
{
    if (self.preTrigger) {
        return self.preTrigger.level + 1;
    }else{
        return 0;
    }
}


- (NSDictionary*)dictAll
{
    NSMutableDictionary* dict = [NSMutableDictionary dictionary];
    
    if (self.guid) {
        dict[KeyTriggerGuid] = self.guid;
    }
    if (self.page) {
        dict[KeyTriggerPage] = self.page;
    }
    if (self.point) {
        dict[KeyTriggerPoint] = self.point;
    }
    
    dict[KeyTriggerLevel] = [NSString stringWithFormat:@"%d",self.level];
    
    if (self.objectType) {
        dict[KeyTriggerObjectType] = self.objectType;
    }
    if (self.objectId) {
        dict[KeyTriggerObjectId] = self.objectId;
    }
    
    if (self.preTrigger) {
        NSDictionary* dictAll = [self.preTrigger dictAll];
        if (dictAll) {
            dict[KeyTriggerPrevious] = dictAll;
        }
    }
    
    return dict;
}

- (NSDictionary*)eventDictPublic
{
    NSArray* keys = @[KeyTriggerPage,KeyTriggerPoint];
    
    NSDictionary* dictAll = [self dictAll];
    NSMutableDictionary* dictResult = [NSMutableDictionary dictionary];
    
    [keys enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
        if ([dictAll objectForKey:obj]) {
            dictResult[obj] = dictAll[obj];
        }
    }];
    
    // 补充路径key
    dictResult[KeyTriggerPath] = [self path];
    
    return dictResult;
}

- (NSDictionary*)eventDictPrivate
{
    NSArray* keys = @[KeyTriggerLevel,KeyTriggerGuid,KeyTriggerObjectId,KeyTriggerObjectType,KeyTriggerPrevious];
    
    NSDictionary* dictAll = [self dictAll];
    NSMutableDictionary* dictResult = [NSMutableDictionary dictionary];
    
    [keys enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
        if ([dictAll objectForKey:obj]) {
            if ([obj isEqualToString:KeyTriggerPrevious]) {
                NSDictionary* pre = dictAll[obj];
//                dictResult[KeyTriggerPrevious] = [[MFWJson jsonWithObject:pre] stringJson];
                dictResult[KeyTriggerPrevious] = [MFWClick stringEncodingJsonObject:pre];
            }else{
                dictResult[obj] = dictAll[obj];
            }
            
        }
    }];
    
    return dictResult;
}

- (NSString*)path
{
    NSMutableString* str = [NSMutableString string];
    if (self.page) {
        [str appendString:self.page];
    }
    
    if (self.preTrigger) {
        [str insertString:@"_" atIndex:0];
        [str insertString:self.preTrigger.path atIndex:0];
    }
    
    return str;
}

-(NSString*)description
{
    return [MFWClick stringEncodingJsonObject:[self dictAll]];
}

@end



//#pragma mark 第三方账号信息
//// 这里是监听MfwShareLogin里的用户信息获取事件
//#define NOTIFICATION_KGETINFO  @"GETINFO"
//#define NOTIFICATION_KGETINFO_KEY_KMESSAGETEXT  @"MESSAGETEXT"
//#define NOTIFICATION_KGETINFO_KEY_ServiceType   @"ServiceType"
//#define NOTIFICATION_KGETINFO_KEY_ServiceType_WeiBoService  @"WeiboService"
//#define NOTIFICATION_KGETINFO_KEY_ServiceType_RenRenService  @"RenRenService"
//#define NOTIFICATION_KGETINFO_KEY_ServiceType_TencentService  @"TencentService"


#pragma mark ------------  SDK 使用

// 页面访问时长的临时字典结构
#define PageViewing_key_start_time  @"start_time"
#define PageViewing_key_page_name   @"page_name"
#define PageViewing_key_trigger     @"trigger"

#pragma mark - 特殊事件的代码
// 启动事件
#define MFWClick_event_code_sys_launch          @"launch"

// 获取到用户方位事件
#define MFWClick_event_code_sys_gps     @"gps"
// 设备信息事件
#define MFWClick_event_code_sys_device  @"device"
// 推送令牌事件
#define MFWClick_event_code_sys_device_token @"device_token"
// 用户信息事件
#define MFWClick_event_code_sys_user    @"user"
// 页面时长统计事件
#define MFWClick_event_code_sys_page    @"page"
// MFWClick错误
#define MFWClick_event_code_mfwclick_error	@"mfwclick_error"

// 应用安装情况（20141102）
#define MFWClick_event_code_app_install @"app_install"


#pragma mark - 事件包结构（字典）对应的两个key
#define MFWClick_event_dict_key_basic   @"basic"
#define MFWClick_event_dict_key_attr    @"attr"


#pragma mark - 事件的基本信息（所有事件均会发送的固定信息）(对应key:basic的值)
#define MFWClick_basic_key_channel_code          @"channel_code"
#define MFWClick_basic_key_device_type           @"device_type"


#define MFWClick_basic_key_event_guid    @"event_guid"
#define MFWClick_basic_key_event_code    @"event_code"
#define MFWClick_basic_key_open_udid     @"open_udid"
#define MFWClick_basic_key_app_code      @"app_code"
#define MFWClick_basic_key_app_ver       @"app_ver"
#define MFWClick_basic_key_sdk_ver       @"sdk_ver"
#define MFWClick_basic_key_launch_guid   @"launch_guid"
#define MFWClick_basic_key_mfwuuid       @"mfwuuid"

// 自1970年01月01日0点到现在的秒数（按UTC+0为准，小数点后精确到6位微秒）
#define MFWClick_basic_key_event_time    @"event_time"
#define MFWClick_basic_key_uid           @"uid"
#define MFWClick_basic_key_device_token  @"device_token"



#pragma mark - 动态信息（特定事件发送）

// 默认填充key、value
#define MFWClick_key_default       @"default"
#define MFWClick_attr_value_default     @"default"

#define MFWClick_key_device_token      @"device_token"

#define MFWClick_key_latitude          @"latitude"
#define MFWClick_key_longitude         @"longitude"
#define MFWClick_key_altitude          @"altitude"
#define MFWClick_key_horizontal_accuracy    @"horizontal_accuracy"
#define MFWClick_key_vertical_accuracy  @"vertical_accuracy"
#define MFWClick_key_course            @"course"
#define MFWClick_key_speed             @"speed"

#define MFWClick_KEY_page_start        @"start"
#define MFWClick_KEY_page_name         @"name"
#define MFWClick_KEY_page_duration     @"duration"
#define MFWClick_key_launch_duration   @"duration"
#define MFWClick_key_launch_time       @"launch_time"
#define MFWClick_key_launch_portrait   @"portrait"

#pragma mark - 设备信息（仅设备信息事件发送）
#define MFWClick_key_time_zone             @"time_zone"
#define MFWClick_key_country_code          @"country_code"
#define MFWClick_key_language_code         @"language_code"
#define MFWClick_key_sys_ver               @"sys_ver"
#define MFWClick_key_device_model          @"device_model"
//#define MFWClick_key_device_id             @"device_id"
#define MFWClick_key_identifier_for_vendor	@"identifier_for_vendor"
#define MFWClick_basic_key_idfv   @"idfv"
#define MFWClick_key_adid                   @"adid"
#define MFWClick_key_mac                    @"mac"
#define MFWClick_key_first                 @"first"


#define MFWClick_key_error_event_code	@"error_event_code"
#define MFWClick_key_error_details      @"error_details"





#pragma mark - 服务端信息

#define API_SERVICE_PUBLIC_KEY @"d41d8cd98f00b204e9800998ecf8427e"
// 事件服务地址
//#define API_SERVICE_MFWClick_URL  @"http://www.mafengwo.cn/mobile/log/interface.php"

//#ifdef DEBUG
//#define API_SERVICE_MFWClick_URL  @"http://weixininterface.mafengwo.com/mobile/event/event_logger.php"
//#else
#define API_SERVICE_MFWClick_URL  @"http://www.mafengwo.cn/mobile/event/event_logger.php"
//#endif

#define NSURL_Request_Timeout     30


#pragma mark - 错误代码
#define MFWClick_Error_Domain   @"MFWClick_Error_Domain"
#define MFWClick_Error_Code_send_failed 1

#pragma mark - 保存在NSUserDefaults里的key
#define MFWClick_NSUserDefaults_key_device_info         @"MFWClick_NSUserDefaults_key_device_info"
#define MFWClick_NSUserDefaults_key_device_token        @"MFWClick_NSUserDefaults_key_device_token"
#define MFWClick_NSUserDefaults_key_event_dict_array    @"MFWClick_NSUserDefaults_key_event_dict_array"
#define MFWClick_NSUserDefaults_key_user_id             @"MFWClick_NSUserDefaults_key_user_id"

#define MFWClick_NSUserDefaults_key_mfwUUID         @"MFWClick_NSUserDefaults_key_mfwUUID2014"



#define MSLog(FORMAT, ...) if(logEnabled){NSString* str = [NSString stringWithFormat:FORMAT,##__VA_ARGS__];NSLog(@"[MFWClick]:%@",str);}






@implementation NSString (Gzip)

- (NSData *)compress
{
    size_t len = [self length];
    size_t bufLen = (len + 12) * 1.001;
    u_char *buf = (u_char *)malloc(bufLen);
    if (buf == NULL) {
        NSLog(@"malloc error");
        return nil;
    }
    int err = compress(buf, &bufLen, (u_char *)[[self dataUsingEncoding:NSUTF8StringEncoding] bytes], len);
    if (err != Z_OK) {
        NSLog(@"compress error");
        free(buf);
        return nil;
    }
    
    NSData *rtn = [[NSData alloc] initWithBytes:buf length:bufLen];
    free(buf);
    
    return rtn;
}


@end


static MFWClick*    _sharedInstance;
static BOOL         logEnabled;
static BOOL         diableCheck;


@interface MFWClick()
{
    dispatch_queue_t    eventBackgroundSendingQueue;
    dispatch_queue_t    eventBackgroundSavingQueue;
    
    BOOL                needResetLaunchGuid;    
}


@property (nonatomic,copy) NSString* appKeyUmeng;

@property (nonatomic,assign) BOOL logEnabled;
@property (nonatomic,assign) MFWClickReportPolicy reportPolicy;
@property (nonatomic,assign) BOOL       alreadySentGPS;
@property (nonatomic,retain) CLLocationManager* locationManager;


@property (nonatomic,retain) NSDate*    launch_time;
@property (nonatomic,retain) NSString*  launch_guid;
@property (nonatomic,retain) NSString*  channel_code;

@property (nonatomic,readonly) NSString* time_zone;
@property (nonatomic,readonly) NSString* country_code;
@property (nonatomic,readonly) NSString* language_code;
@property (nonatomic,readonly) NSString* app_code;
@property (nonatomic,readonly) NSString* app_ver;
@property (nonatomic,readonly) NSString* sys_ver;
@property (nonatomic,readonly) NSString* device_model;
//@property (nonatomic,readonly) NSString* device_id;
@property (nonatomic,readonly) NSString* identifier_for_vendor;
@property (nonatomic,readonly) NSString* open_udid;

@property (nonatomic,retain) NSString* uid;

#pragma mark - 内部使用方法
@property (nonatomic,retain) NSMutableArray* arrayPageViewing;
// 当应用切换出去时，会将当前正处于浏览状态的页面名称记录在这个数组里，在切换回来时自动重新beginLogPageView
@property (nonatomic,retain) NSMutableArray* arrayPageViewNameWillResume;
// 当前正在发送的事件
@property (nonatomic,retain) NSArray* arraySendingEventDict;


// 默认为实时发送、官方应用商店(iOS为 App Store)
+ (MFWClick*)sharedInstance;

#pragma 设置初始参数
//// channelCode 为nil或@""时,被当作@"App Store"渠道
//- (void)startWithReportPolicy:(MFWClickReportPolicy)rp channelCode:(NSString*)channelCode;


#pragma mark 特殊事件记录
// 记录设备的deviceToken
- (void)eventDeviceTokenIfChanged:(NSData*)deviceTokenData;
// 记录设备的经纬度坐标
//- (void)eventGPSWithLatitude:(double)latitude longitude:(double)longitude;
- (void)eventGPSWithLocation:(CLLocation*)newLocation;


#pragma mark 通用事件记录

// 通用事件
//- (void)beginLogPageView:(NSString*)pageName;
//- (void)endLogPageView:(NSString*)pageName;
- (void)event:(NSString *)eventCode attributes:(NSDictionary *)attributes;


#pragma mark 辅助方法
- (NSDictionary*)packEventDictWithEventCode:(NSString*)eventCode attributes:(NSDictionary*)attrDict;
- (void)ready2SendEventDict:(NSDictionary*)eventDict;

@end

@implementation MFWClick

+ (MFWClick*)sharedInstance
{
    if(_sharedInstance == nil)
    {
        MSLog(@"未启动事件记录系统");
    }
    return  _sharedInstance;
}

-(MFWClick*)init
{
    self = [super init];
    if(self)
    {
        eventBackgroundSendingQueue = dispatch_queue_create("cn.mafengwo.MFWClick.eventBackgroundSendingQueue", NULL);
        eventBackgroundSavingQueue = dispatch_queue_create("cn.mafengwo.MFWClick.eventBackgroundSavingQueue", NULL);
        
        // 注册应用切回主屏幕、返回应用事件
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationWillResignActive) name:UIApplicationWillResignActiveNotification object:nil];
        
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidBecomeActive) name:UIApplicationDidBecomeActiveNotification object:nil];
        
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidEnterBackground) name:UIApplicationDidEnterBackgroundNotification object:nil];
        
        
    }
    
    return self;
}

#pragma mark ------ 内部实现 ------

#pragma mark 注册通知

- (void)applicationDidEnterBackground
{
    NSLog(@"start applicationDidEnterBackground");
    
    NSLog(@"do something");
    NSLog(@"end applicationDidEnterBackground");
}
- (void)applicationWillResignActive
{
    NSLog(@"applicationWillResignActive");
    // 发送本次启动使用时长
    if(self.launch_time)
    {
        MSLog(@"发送本次启动使用时长");
        NSTimeInterval launch_duration = [[NSDate date] timeIntervalSinceDate:self.launch_time];
        [self event:MFWClick_event_code_sys_launch
         attributes:
         @{MFWClick_key_launch_duration:[NSString stringWithFormat:@"%f",launch_duration]
         ,MFWClick_key_launch_time:[NSString stringWithFormat:@"%f",[self.launch_time timeIntervalSince1970]]
         }
         ];
    }
    
    // 发送当前正在记录的页面时长统计
    MSLog(@"发送当前正在记录的页面时长统计");
    
    NSMutableArray* arrayResume = [NSMutableArray array];
    for (NSDictionary* dict in self.arrayPageViewing) {
        NSString* pageName = [dict objectForKey:PageViewing_key_page_name];
        NSString* trigger = [dict objectForKey:PageViewing_key_trigger];
        NSMutableDictionary* resumeDict = [NSMutableDictionary dictionary];
        if (pageName) {
            resumeDict[PageViewing_key_page_name] = pageName;
        }
        if (trigger) {
            resumeDict[PageViewing_key_trigger] = trigger;
        }

        [arrayResume addObject:resumeDict];
    }
    
    [arrayResume enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
        NSDictionary* dict = obj;
        NSString* pageName = [dict objectForKey:PageViewing_key_page_name];
        MFWClickTrigger* trigger = [dict objectForKey:PageViewing_key_trigger];
        [MFWClick endLogPageViewWithTrigger:trigger pageName:pageName];
    }];
    
    
    // 记录下来哪些页面，留待切换回应用时，重新开始计时
    MSLog(@"记录下来哪些页面，留待切换回应用时，重新开始计时");
    self.arrayPageViewNameWillResume = arrayResume;

    
    // 标记下，让下一次应用active的时候，重置launch_guid
    needResetLaunchGuid = YES;
    
    if (self.reportPolicy == MFWClickReportPolicyOnExit) {
        __block UIBackgroundTaskIdentifier taskID ;
        taskID = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
            [[UIApplication sharedApplication] endBackgroundTask:taskID];
            taskID = UIBackgroundTaskInvalid;
        }];
        if (taskID == UIBackgroundTaskInvalid) {
            return;
        }
        
        // 如果发送策略是退出时发送
        [self sendLocalEvents];
    }
}
- (void)applicationDidBecomeActive
{
    if(needResetLaunchGuid)
    {
        // 设置本次启动时的GUID，用于区别事件日志属于哪一次启动
        MSLog(@"重置本次启动时的GUID");
        self.launch_guid = [MFWClick GetUUID];
        // 记录下启动时间
        self.launch_time = [NSDate date];
    }
    
    // 恢复页面时长统计
    if(self.arrayPageViewNameWillResume.count > 0)
    {
        MSLog(@"恢复页面时长统计");
        [self.arrayPageViewNameWillResume enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
            NSDictionary* dict = obj;
            
            NSString* pageName = [dict objectForKey:PageViewing_key_page_name];
            MFWClickTrigger* trigger = [dict objectForKey:PageViewing_key_trigger];
            [MFWClick beginLogPageViewWithTrigger:trigger pageName:pageName];
        }];
        self.arrayPageViewNameWillResume = nil;
    }
    
    // 开始尝试更新用户方位信息（只在用户允许开启GPS时有效）
    [self startUpdatingGPS];
}

#pragma mark ------ 辅助方法

#pragma mark JSON
+ (id)parseJsonData:(NSData*)data;
{
    if (data)
    {
        return [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
    }
    
    return nil;
}

+ (id)parseJsonString:(NSString *)jsonStr
{
    return [self parseJsonData:[jsonStr dataUsingEncoding:NSUTF8StringEncoding]];
}

+ (NSData*)dataEncodingJsonObject:(id)obj;
{
    return [NSJSONSerialization dataWithJSONObject:obj options:kNilOptions error:nil];
}

+ (NSString*)stringEncodingJsonObject:(id)obj;
{
    if (!obj)
    {
        return nil;
    }
    NSData *jsonRawData = [self dataEncodingJsonObject:obj];
    return [[NSString alloc] initWithData:jsonRawData encoding:NSUTF8StringEncoding];
}


#pragma mark base64
static const char *kBase64EncodeChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
//static const char *kWebSafeBase64EncodeChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
static const char kBase64PaddingChar = '=';
//static const char kBase64InvalidChar = 99;

#if !defined(GTM_INLINE)
#if defined (__GNUC__) && (__GNUC__ == 4)
#define GTM_INLINE static __inline__ __attribute__((always_inline))
#else
#define GTM_INLINE static __inline__
#endif
#endif


GTM_INLINE NSUInteger CalcEncodedLength(NSUInteger srcLen, BOOL padded) {
	NSUInteger intermediate_result = 8 * srcLen + 5;
	NSUInteger len = intermediate_result / 6;
	if (padded) {
		len = ((len + 3) / 4) * 4;
	}
	return len;
}

+(NSUInteger)baseEncode:(const char *)srcBytes
                 srcLen:(NSUInteger)srcLen
              destBytes:(char *)destBytes
                destLen:(NSUInteger)destLen
                charset:(const char *)charset
                 padded:(BOOL)padded {
	if (!srcLen || !destLen || !srcBytes || !destBytes) {
		return 0;
	}
	
	char *curDest = destBytes;
	const unsigned char *curSrc = (const unsigned char *)(srcBytes);
	
	// Three bytes of data encodes to four characters of cyphertext.
	// So we can pump through three-byte chunks atomically.
	while (srcLen > 2) {
		// space?
//		_GTMDevAssert(destLen >= 4, @"our calc for encoded length was wrong");
		curDest[0] = charset[curSrc[0] >> 2];
		curDest[1] = charset[((curSrc[0] & 0x03) << 4) + (curSrc[1] >> 4)];
		curDest[2] = charset[((curSrc[1] & 0x0f) << 2) + (curSrc[2] >> 6)];
		curDest[3] = charset[curSrc[2] & 0x3f];
		
		curDest += 4;
		curSrc += 3;
		srcLen -= 3;
		destLen -= 4;
	}
	
	// now deal with the tail (<=2 bytes)
	switch (srcLen) {
		case 0:
			// Nothing left; nothing more to do.
			break;
		case 1:
			// One byte left: this encodes to two characters, and (optionally)
			// two pad characters to round out the four-character cypherblock.
//			_GTMDevAssert(destLen >= 2, @"our calc for encoded length was wrong");
			curDest[0] = charset[curSrc[0] >> 2];
			curDest[1] = charset[(curSrc[0] & 0x03) << 4];
			curDest += 2;
			destLen -= 2;
			if (padded) {
//				_GTMDevAssert(destLen >= 2, @"our calc for encoded length was wrong");
				curDest[0] = kBase64PaddingChar;
				curDest[1] = kBase64PaddingChar;
				curDest += 2;
			}
			break;
		case 2:
			// Two bytes left: this encodes to three characters, and (optionally)
			// one pad character to round out the four-character cypherblock.
//			_GTMDevAssert(destLen >= 3, @"our calc for encoded length was wrong");
			curDest[0] = charset[curSrc[0] >> 2];
			curDest[1] = charset[((curSrc[0] & 0x03) << 4) + (curSrc[1] >> 4)];
			curDest[2] = charset[(curSrc[1] & 0x0f) << 2];
			curDest += 3;
			destLen -= 3;
			if (padded) {
//				_GTMDevAssert(destLen >= 1, @"our calc for encoded length was wrong");
				curDest[0] = kBase64PaddingChar;
				curDest += 1;
			}
			break;
	}
	// return the length
	return (curDest - destBytes);
}


+(NSData *)baseEncode:(const void *)bytes
               length:(NSUInteger)length
              charset:(const char *)charset
               padded:(BOOL)padded {
	// how big could it be?
	NSUInteger maxLength = CalcEncodedLength(length, padded);
	// make space
	NSMutableData *result = [NSMutableData data];
	[result setLength:maxLength];
	// do it
	NSUInteger finalLength = [self baseEncode:bytes
									   srcLen:length
									destBytes:[result mutableBytes]
									  destLen:[result length]
									  charset:charset
									   padded:padded];
	if (finalLength) {
//		_GTMDevAssert(finalLength == maxLength, @"how did we calc the length wrong?");
	} else {
		// shouldn't happen, this means we ran out of space
		result = nil;
	}
	return result;
}


+(NSData *)encodeData:(NSData *)data {
	return [self baseEncode:[data bytes]
					 length:[data length]
					charset:kBase64EncodeChars
					 padded:YES];
}

- (NSString*)base64:(NSString*)str
{
    NSData *data = [str dataUsingEncoding:NSUTF8StringEncoding allowLossyConversion:YES];
    data = [MFWClick encodeData:data];
    NSString *returnStr = [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding];
    
    return returnStr;
}
-(NSString *)md5Digest:(NSString *)str
{
    const char *cStr = [str UTF8String];
    unsigned char result[CC_MD5_DIGEST_LENGTH];
    CC_MD5(cStr, strlen(cStr), result);
    return [[NSString stringWithFormat:
             @"%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
             result[0], result[1], result[2], result[3], result[4], result[5], result[6], result[7],
             result[8], result[9], result[10], result[11], result[12], result[13], result[14], result[15]
             ] lowercaseString];
}

#pragma mark 事件打包
- (NSDictionary*)packEventDictWithEventCode:(NSString*)eventCode attributes:(NSDictionary*)attrDict
{
    if(attrDict.count == 0)
    {
        // 如果没有，则默认用事件代码生成一个事件属性
        attrDict = @{MFWClick_key_default: MFWClick_attr_value_default};
    }
    
    // 将所有key替换为小写
    NSMutableDictionary* attrDictWithLowercaseKey = [NSMutableDictionary dictionaryWithCapacity:0];
    for (NSString* key in attrDict.allKeys) {
        [attrDictWithLowercaseKey setObject:[attrDict objectForKey:key] forKey:key.lowercaseString];
    }
    
    // 补充基本的信息
    NSMutableDictionary* basicDict = [NSMutableDictionary dictionaryWithDictionary:@{
                                                                                     MFWClick_basic_key_device_type  : @"ios"
                                                                                     ,MFWClick_basic_key_app_code    : [self app_code]
                                                                                     
                                                                                     ,MFWClick_basic_key_channel_code: [self channel_code]
                                                                                     ,MFWClick_basic_key_app_ver     : [self app_ver]
                                                                                     ,MFWClick_key_sys_ver           : [self sys_ver]
                                                                                     ,MFWClick_basic_key_sdk_ver     : MFWClick_SDK_VERSION
                                                                                     ,MFWClick_basic_key_launch_guid : [self launch_guid]
                                                                                     ,MFWClick_basic_key_open_udid   : [self open_udid]
                                                                                     ,MFWClick_basic_key_idfv: [self idfv]
                                                                                     ,MFWClick_basic_key_event_code  : eventCode
                                                                                     ,MFWClick_basic_key_event_guid  : [MFWClick GetUUID]
                                                                                     ,MFWClick_basic_key_event_time  : [NSString stringWithFormat:@"%f",((NSDate*)[NSDate date]).timeIntervalSince1970]
                                                                                     }];
    
    // 补充已登录用户id
    NSString* uid = [self userId];
    if (uid.length > 0) {
        [basicDict setObject:uid forKey:MFWClick_basic_key_uid];
    }
    
    // 补充推送设备令牌
    NSString* deviceToken = [self device_token];
    if (deviceToken) {
        [basicDict setObject:deviceToken forKey:MFWClick_basic_key_device_token];
    }
    
    // 补充mfwuuid
    NSString* mfwuuid = [self mfwUUID];
    if (mfwuuid.length > 0) {
        [basicDict setObject:mfwuuid forKey:MFWClick_basic_key_mfwuuid];
    }
    
#ifdef DEBUG
    if (uid.length > 0) {
//        NSLog(@"MFWClick中有uid:%@",uid);
    }else{
//        NSLog(@"MFWClick中无uid");
    }
    
    NSDictionary* eventDict = @{@"debug":@"1",MFWClick_event_dict_key_basic: basicDict,MFWClick_event_dict_key_attr:attrDictWithLowercaseKey};
#else
    NSDictionary* eventDict = @{MFWClick_event_dict_key_basic: basicDict,MFWClick_event_dict_key_attr:attrDictWithLowercaseKey};
#endif
    
    return eventDict;
}


- (NSArray*)localEventDictArray
{
    if(!_arraySendingEventDict)
    {
        NSArray* sendingArray = [[NSUserDefaults standardUserDefaults] objectForKey:MFWClick_NSUserDefaults_key_event_dict_array];
        if(sendingArray.count > 0)
        {
//            _arraySendingEventDict = [[NSMutableArray arrayWithArray:sendingArray]retain];
            self.arraySendingEventDict = [NSMutableArray arrayWithArray:sendingArray];
        }
        else
        {
//            _arraySendingEventDict = [[NSMutableArray alloc]init];
            self.arraySendingEventDict = [NSMutableArray arrayWithCapacity:0];
        }
    }
    
    return _arraySendingEventDict;
}


-(NSString*)getSigningWithString:(NSString*)str serviceAPIKey:(NSString*)apiKey
{
    NSString *signString = [NSString stringWithFormat: @"%@%@",str, apiKey];
//    signString  = [signString stringByReplacingOccurrencesOfString: @"/" withString: @"\\/"]; // 这是一个莫名其妙的代码，不应该执行，会导致签名计算错误！！！
    NSString *sign = [self md5Digest: signString];
    return sign;
}




// 将事件放到数组里，统一发送到服务端事件记录接口
- (void)sendEventDictArrayInBackground:(NSArray*)eventDictArray completion:(void(^)(BOOL success,NSError* error))handler
{
    if(eventDictArray.count == 0)
    {
        return;
    }

    dispatch_async(eventBackgroundSendingQueue, ^(){
        BOOL success = YES;
        NSError* error = nil;
        
        // 保存下来，用于应用突然被终止时，下次启动重新发送
        [self addEventDictArray2Local:eventDictArray];
        
        // 考虑到会有中文出现，对json整体做base64编码,作为最终的数据发送
        NSString* json = [MFWClick stringEncodingJsonObject:eventDictArray];
//#ifdef DEBUG
//        json = @"[{\"attr\":{\"start\":\"1373016002.003169\",\"name\":\"消息中心\",\"duration\":\"52.689485\"},\"debug\":\"1\",\"basic\":{\"channel_code\":\"\",\"app_ver\":\"5.0\",\"sdk_ver\":\"20130629\",\"launch_guid\":\"38EE3910CCC34850BD4D5E78CEB7F60B\",\"open_udid\":\"a9ca819a3ec2e8df6866b9ea085b10ec7a693145\",\"event_code\":\"page\",\"event_guid\":\"75A67199F06E4A26A4EE3259F14590B9\",\"device_type\":\"ios\",\"event_time\":\"1373016002.003217\",\"uid\":\"\",\"app_code\":\"cn.mafengwo.www\"}}]";
//#endif
        
        NSString* base64_json = [self base64:json];
        
//#ifdef DEBUG // todo for hanxin 测试压缩（需要和服务端定好参数服务端支持）
//        NSString* base64_compressed_json = [[NSString alloc] initWithData:[MFWClick encodeData:json.compress] encoding: NSUTF8StringEncoding];
//        NSLog(@"json:%d,compressed:%d",base64_json.length,base64_compressed_json.length);
//#endif

        
        // 计算签名
//        NSString *signString = [NSString stringWithFormat: @"%@%@",base64_json, API_SERVICE_PUBLIC_KEY];
//        signString  = [signString stringByReplacingOccurrencesOfString: @"/" withString: @"\\/"];
//        NSString *sign = [self md5Digest: signString];
        NSString *sign = [self getSigningWithString:base64_json serviceAPIKey:API_SERVICE_PUBLIC_KEY];
        
//#ifdef DEBUG
//        sign = @"453ced078b377b36157b5a7026b33ecd";
//#endif
        
        // 生成post字符串
        NSDictionary *bdic = [[NSDictionary alloc] initWithObjects:
                               [NSArray arrayWithObjects: base64_json, sign, nil]
                                                            forKeys:
                               [NSArray arrayWithObjects: @"data", @"sign", nil]];
        
        NSString *postString = [NSString stringWithFormat: @"r=%@",[MFWClick stringEncodingJsonObject:bdic]];
        postString  = [postString stringByReplacingOccurrencesOfString: @"+" withString: @"%2B"];
        
        
        // 生成request
        NSMutableData *postBody = [NSMutableData data];
        NSMutableURLRequest *theRequest = [[NSMutableURLRequest alloc] init];
        NSString* urlString = API_SERVICE_MFWClick_URL;
        NSURL *url = [NSURL URLWithString: urlString];
        
        [theRequest setURL:url];
        [theRequest setHTTPMethod:@"POST"];
        [theRequest setTimeoutInterval:NSURL_Request_Timeout];
        [postBody appendData:[postString dataUsingEncoding: NSUTF8StringEncoding allowLossyConversion:YES]];
        [theRequest setHTTPBody:postBody];
        MSLog(@"[url:%@][post(%d):%@]", urlString, postString.length ,postString);

        
        // 发送到服务端
        NSError* returnError = nil;
        NSData *returnData = [NSURLConnection sendSynchronousRequest:theRequest returningResponse:nil error:&returnError];
        if (returnError != nil) {
            success = NO;
            error = returnError;
        }else{
            NSDictionary *returnDict = nil;
            int retValue = 0;

            if (returnData) {
                NSString* returnStr = [[NSString alloc] initWithData: returnData encoding: NSUTF8StringEncoding];
                
                MSLog(@"[url:%@][return(%d):%@]", urlString, returnStr.length ,returnStr);
                
                returnDict = [MFWClick parseJsonString:returnStr];
                if ([returnDict isKindOfClass:[NSDictionary class]]) {
                    retValue = [[[returnDict objectForKey:@"data"] objectForKey:@"ret"] intValue];
                }
            }
            


            
//#ifdef DEBUG
//            NSString* dataJson = [[returnDict objectForKey:@"data"] JSONRepresentation];
//            NSString* retSignLocal = [self getSigningWithString:dataJson serviceAPIKey:API_SERVICE_PUBLIC_KEY];
//            NSString* retSignRemote = [returnDict objectForKey:@"sign"];
//            MSLog(@"sign[%@] local:%@ remote:%@",[NSNumber numberWithBool:[retSignLocal isEqualToString:retSignRemote]],retSignLocal,retSignRemote);
//#endif
            
            if (retValue != 1)
            {
                success = NO;
                NSString* errorMessage = [[returnDict objectForKey:@"data"] objectForKey:@"message"];
                NSDictionary* errorDict = nil;
                if(errorMessage.length > 0)
                {
                    errorDict = @{@"message":errorMessage};
                }
                error = [NSError errorWithDomain:MFWClick_Error_Domain code:MFWClick_Error_Code_send_failed userInfo:errorDict];
            }
        }
        
        if(success)
        {
            // 已完成发送，从本地移除
            [self removeEventDictArrayFromLocal:eventDictArray];
            
            // 对于某些事件，在发送成功后特殊处理下
            for (NSDictionary* eventDict in eventDictArray)
            {
                NSDictionary* basicDict = [eventDict objectForKey:MFWClick_event_dict_key_basic];
                NSDictionary* attrDict = [eventDict objectForKey:MFWClick_event_dict_key_attr];
                
                if([[basicDict objectForKey:MFWClick_basic_key_event_code] isEqualToString:MFWClick_event_code_sys_device])
                {
                    // 保存发送成功的设备信息到本地
                    [self saveSentDeviceInfoAttrDict:attrDict];
                }
                
                if ([[basicDict objectForKey:MFWClick_basic_key_event_code] isEqualToString:MFWClick_event_code_sys_device_token])
                {
                    // 保存发送成功的deviceToken到本地
                    [self saveSentDeviceToken:[attrDict objectForKey:MFWClick_key_device_token]];
                }
            }
                        
            MSLog(@"[success]发送到服务端:%@",eventDictArray);
        }
        else
        {
            MSLog(@"[failed]发送到服务端[%@]:%@",error,eventDictArray);
        }
        
        if(handler)
        {
            handler(success,error);
        }
    });
}


- (void)ready2SendEventDict:(NSDictionary*)eventDict
{
    if (eventDict.count == 0) {
        return;
    }
    
    NSArray* eventDictArray = @[eventDict];
    
    switch (self.reportPolicy) {
        case MFWClickReportPolicyRealTime:
            // 立即发送
            MSLog(@"根据发送策略，立即发送[%d][%d]",eventDict.description.length,@"test".length);
            [self sendEventDictArrayInBackground:eventDictArray completion:nil];
            break;
        case MFWClickReportPolicyOnExit:
            // 保存下来，留待退出时发送
            MSLog(@"根据发送策略，下次发送[%d]",eventDict.description.length);
            [self addEventDictArray2Local:eventDictArray];
        default:
            MSLog(@"未知的发送策略:%d",self.reportPolicy);
            break;
    }
}

#pragma mark 调试参数
// 用于调试时是否打印出日志
+ (void)setLogEnabled:(BOOL)value
{
    logEnabled = value;
    // 设置友盟
//    [MobClick setLogEnabled:value];
}
+ (void)setDisableCheck:(BOOL)value
{
    diableCheck = value;
}

#pragma mark 判断是否配置

+(BOOL)umengEnabled
{
    return [[self sharedInstance] umengEnabled];
}
-(BOOL)umengEnabled
{
    return self.appKeyUmeng.length > 0;
}

#pragma 设置初始参数


// channelCode 为nil或@""时,被当作@"App Store"渠道
+ (void)startWithReportPolicy:(MFWClickReportPolicy)rp
                  channelCode:(NSString*)channelCode
                  umengAppKey:(NSString*)umengAppKey
            talkingDataAppKey:(NSString*)talkingDataAppKey
{
    if (channelCode.length == 0) {
        channelCode = @"";
    }
    
    if(umengAppKey.length == 0){
#ifdef DEBUG
        // 专门用于测试的umengAppKey
        umengAppKey = @"50b57fbd5270157ca3000011";
#else
        umengAppKey = @"";
#endif
    }

    // umeng默认取 |CFBundleVersion|, 为了保持和appstore上用户看到的version一致，需要设置一下
    
    
    
    /* ************** umeng基础参数配置  暂时备注
     [MobClick setAppVersion:XcodeAppVersion];
     
     [UMAnalyticsConfig sharedInstance].appKey = umengAppKey;
     [UMAnalyticsConfig sharedInstance].channelId = channelCode;
     [UMAnalyticsConfig sharedInstance].bCrashReportEnabled = YES;
  
//    [UMAnalyticsConfig sharedInstance].secret = 
    // 启动友盟
    if (umengAppKey.length > 0) {
        switch (rp) {
            case MFWClickReportPolicyRealTime:
                [UMAnalyticsConfig sharedInstance].ePolicy = REALTIME;
                [MobClick startWithConfigure:[UMAnalyticsConfig sharedInstance]];
                break;
            case MFWClickReportPolicyOnExit:
                [UMAnalyticsConfig sharedInstance].ePolicy = BATCH;
                [MobClick startWithConfigure:[UMAnalyticsConfig sharedInstance]];
                break;
            default:
                break;
        }
    }else{
#ifdef DEBUG
        [NSException raise:@"未配置友盟统计平台" format:@"请使用新的startWithReportPolicy方法，并确保umengAppKey的值有效"];
#endif
    }
     */

    
    
    if (_sharedInstance == nil)
    {
        _sharedInstance = [[MFWClick alloc] init];
    }
    
    [MFWClick sharedInstance].appKeyUmeng = umengAppKey;
    
    // 启动MFWClick
    [[MFWClick sharedInstance] startWithReportPolicy:rp channelCode:channelCode];
}

#pragma mark ------ 设置用户id
+ (void)setUserId:(NSString*)uid
{
    [[MFWClick sharedInstance] setUserId:uid];
}

#pragma mark 特殊事件记录
// 记录设备的deviceToken
+ (void)eventDeviceTokenIfChanged:(NSData*)deviceTokenData
{
    if (deviceTokenData.length == 0)
    {
        MSLog(@"deviceTokenData为nil，无法发送");
        return;
    }
    
    NSString *deviceToken = [NSString stringWithFormat:@"%@", deviceTokenData];
    deviceToken = [deviceToken stringByReplacingOccurrencesOfString: @"<" withString: @""];
    deviceToken = [deviceToken stringByReplacingOccurrencesOfString: @">" withString: @""];
    deviceToken = [deviceToken stringByReplacingOccurrencesOfString: @" " withString: @""];
    MSLog(@"deviceToken:%@",deviceToken);

    if ([self umengEnabled])
    {
        NSString* alreadySent = [[NSUserDefaults standardUserDefaults] objectForKey:MFWClick_NSUserDefaults_key_device_token];
        if([deviceToken isEqualToString:alreadySent])
        {
            MSLog(@"deviceToken未发生变化，无需再发送");
        }
        else if(alreadySent.length == 0)
        {
            MSLog(@"deviceToken首次发送");
//            [MobClick event:MFWClick_event_code_sys_device_token label:@"首次"];
        }
        else
        {
            MSLog(@"deviceToken发生变化");
//            [MobClick event:MFWClick_event_code_sys_device_token label:@"更新"];
        }
    }
//    [[MFWClick sharedInstance] eventDeviceTokenIfChanged:deviceTokenData];
}


#pragma mark 通用事件记录

// 通用事件
+ (void)beginLogPageView:(NSString*)pageName
{
    [MFWClick beginLogPageViewWithTrigger:nil pageName:pageName];
}
+ (void)beginLogPageViewWithTrigger:(MFWClickTrigger*)trigger pageName:(NSString*)pageName
{
    // 友盟记录
    if([self umengEnabled])
    {
//        [MobClick beginLogPageView:pageName];
    }
    
//    [[MFWClick sharedInstance] beginLogPageViewWithTrigger:trigger pageName:pageName];
}

+(void)endLogPageViewWithTrigger:(MFWClickTrigger*)trigger pageName:(NSString *)pageName
{
    [self checkChinese:pageName];
    
    // 友盟记录
    if([self umengEnabled])
    {
//        [MobClick endLogPageView:pageName];
    }
    
//    __weak MFWClick* wself = [MFWClick sharedInstance];
//
//    
//    // 根据访问起始时间，计算总的访问时间，并发送事件
//    for (NSDictionary* pageStartDict in wself.arrayPageViewing) {
//        NSString* tmpPageName = [pageStartDict objectForKey:PageViewing_key_page_name];
//        MFWClickTrigger* tmpTrigger = [pageStartDict objectForKey:PageViewing_key_trigger];
//        
//        // 找到第一个匹配的页面记录
//        if([tmpPageName isEqualToString:pageName] && (tmpTrigger == trigger))
//        {
//            NSDate* tmpDate = [pageStartDict objectForKey:PageViewing_key_start_time];
//            NSDate* nowDate = [NSDate date];
//            // 计算页面停留时长：当前停留时长
//            NSTimeInterval pageViewingTimeInterval = [nowDate timeIntervalSinceDate:tmpDate];
//            NSString* page_start = [NSString stringWithFormat:@"%f",[nowDate timeIntervalSince1970]];
//            NSString* page_duration = [NSString stringWithFormat:@"%f",pageViewingTimeInterval];
//            
//            // 移除此页面记录
//            [wself.arrayPageViewing removeObject:pageStartDict];
//            
//            // 发送页面时长统计事件
//            NSDictionary* privateDict = @{MFWClick_KEY_page_start:page_start
//                                          ,MFWClick_KEY_page_duration:page_duration
//                                          };
//
//            UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
//            NSString *portrait = @"1";
//            if (UIInterfaceOrientationIsPortrait(orientation))
//            {
//                portrait = @"1";
//            }
//            else
//            {
//                portrait = @"0";
//            }
//            
//            NSDictionary* publicDict = @{MFWClick_KEY_page_name:pageName,
//                                         MFWClick_key_launch_portrait:portrait};
//            
//            [MFWClick event:MFWClick_event_code_sys_page trigger:trigger publicAttributesBlock:^NSDictionary *{
//                return publicDict;
//            } privateAttributesBlock:^NSDictionary *{
//                return privateDict;
//            }];
//            
//            return;
//        }
//    }
//    
//    NSString* message = [NSString stringWithFormat:@"未找到页面信息[%@],请确认在调用endLogPageView前已调用beginLogPageView方法",pageName];
//    
//#ifdef DEBUG
//    UIAlertView* alert = [[UIAlertView alloc] initWithTitle:@"MFWClick错误" message:message delegate:nil cancelButtonTitle:@"确认" otherButtonTitles:nil, nil];
//    [alert show];
//#else
//    NSLog(@"%@", message);
//#endif

}

+ (void)endLogPageView:(NSString*)pageName
{
    [self endLogPageViewWithTrigger:nil pageName:pageName];
}


//+ (void)event:(NSString *)eventCode
//{
//    if ([self umengEnabled]) {
//        [MobClick event:eventCode];
//    }
//    [[MFWClick sharedInstance] event:eventCode attributes:nil];
//}
//+ (void)event:(NSString *)eventCode label:(NSString *)label
//{
//    if([self umengEnabled])
//    {
//        [MobClick event:eventCode label:label];
//    }
//    
//    // 生成一个事件属性，以eventCode作为事件属性的key，label为值
//    NSDictionary* attrDict = @{MFWClick_key_default : label};
//    [[MFWClick sharedInstance] event:eventCode attributes:attrDict];
//}

//+ (void)event:(NSString *)eventCode attributes:(NSDictionary *)attributes
//{
//    if ([self umengEnabled]) {
//        [NSException raise:@"接口调用异常" format:@"已开启友盟同步记录事件，请使用带参数umengIgnoreKeys的同名接口event:attributes:umengIgnoreKeys:替换此次调用的接口event:attributes:"];
//    }
//    [[MFWClick sharedInstance] event:eventCode.lowercaseString attributes:attributes];
//}

+(void)checkChinese:(NSString*)pageName
{
#ifdef DEBUG
    if (!diableCheck) {
        // 校验页面名称，如果没有汉字，则弹Alert
        BOOL hasChinese = NO;
        for (int i=0; i<pageName.length; ++i)
        {
            NSRange range = NSMakeRange(i, 1);
            NSString *subString = [pageName substringWithRange:range];
            const char *cString = [subString UTF8String];
            if (strlen(cString) == 3)
            {
                hasChinese = YES;
                break;
            }
        }
        
        if (!hasChinese) {
            UIAlertView* alertView = [[UIAlertView alloc] initWithTitle:@"军情急报" message:[NSString stringWithFormat:@"大王，[%@]这个页面是不是应该有个中文名字呢？",pageName] delegate:nil cancelButtonTitle:@"先退下吧" otherButtonTitles:nil, nil];
            [alertView show];
        }
    }
#endif
}

+(void)event:(NSString *)eventCode trigger:(MFWClickTrigger*)trigger publicAttributesBlock:(NSDictionary*(^)(void))pubAttrBlock privateAttributesBlock:(NSDictionary*(^)(void))priAttrBlock
{
    [MFWClick event:eventCode publicAttributesBlock:^NSDictionary *{
        NSMutableDictionary* dict = [NSMutableDictionary dictionary];
        if (pubAttrBlock) {
            NSDictionary* pub = pubAttrBlock();
            [dict addEntriesFromDictionary:pub];
        }
        if (trigger) {
            NSDictionary* triggerPub = [trigger eventDictPublic];
            [dict addEntriesFromDictionary:triggerPub];
            [self checkChinese:trigger.page];
        }
        return dict;
        
    } privateAttributesBlock:^NSDictionary *{
        NSMutableDictionary* dict = [NSMutableDictionary dictionary];
        if (priAttrBlock) {
            NSDictionary* pri = priAttrBlock();
            [dict addEntriesFromDictionary:pri];
        }
        if (trigger) {
            NSDictionary* triggerPri = [trigger eventDictPrivate];
            [dict addEntriesFromDictionary:triggerPri];
        }
        return dict;
    }];
}


+ (void)event:(NSString *)eventCode publicAttributesBlock:(NSDictionary*(^)(void))pubAttrBlock privateAttributesBlock:(NSDictionary*(^)(void))priAttrBlock
{
    @try {
        
        NSDictionary* pubAttr = nil;
        if(pubAttrBlock)
        {
            pubAttr = pubAttrBlock();
        }
        
        NSDictionary* priAttr = nil;
        if(priAttrBlock)
        {
            priAttr = priAttrBlock();
        }
    
        // 校验是否有重复key
        for (NSString* publicKey in pubAttr.allKeys) {
            for (NSString* privateKey in priAttr.allKeys)
            {
                if ([publicKey isEqualToString:privateKey])
                {
#ifdef DEBUG
                    [NSException raise:@"异常event:publicAttributes:privateAttributes:" format:@"在privateAttr中发现重复的key:%@",publicKey];
#endif
                }
            }
        }
        
        
        // 发送友盟
        if (![self umengEnabled]) {
            [NSException raise:@"接口调用异常" format:@"未开启友盟同步记录事件"];
        }else{
            /*
             if (pubAttr.count == 0) {
             [MobClick event:eventCode];
             }else{
             [MobClick event:eventCode attributes:pubAttr];
             }
             */
        }
        
//        // 发送MFWClick
//        NSMutableDictionary* dictAll = [NSMutableDictionary dictionaryWithCapacity:0];
//        if (pubAttr) {
//            [dictAll addEntriesFromDictionary:pubAttr];
//        }
//        if (priAttr) {
//            [dictAll addEntriesFromDictionary:priAttr];
//        }
//        
//        [[MFWClick sharedInstance] event:eventCode.lowercaseString attributes:dictAll];

    }
    @catch (NSException *exception) {
        NSString* str = exception.description;
        if (str == nil) {
            str = @"";
        }
        if (eventCode == nil) {
            eventCode = @"";
        }
        
#ifdef DEBUG
        MSLog(@"MFWClick日志异常:%@",str);
        UIAlertView* alertView = [[UIAlertView alloc]initWithTitle:@"MFWClick日志异常" message:str delegate:self cancelButtonTitle:@"确认" otherButtonTitles:nil, nil];
        [alertView show];
#else
        // 记录错误日志
        [self event:MFWClick_event_code_mfwclick_error
              publicAttributesBlock:^NSDictionary *{
                  return @{MFWClick_key_error_event_code:eventCode};
              }
              privateAttributesBlock:^NSDictionary *{
                  return @{MFWClick_key_error_details:str};
              }];
#endif
    }
}

//+ (void)event:(NSString *)eventCode attributes:(NSDictionary *)attributes umengIgnoreKeys:(NSArray*)ignoreKeys
//{
//    if ([ignoreKeys isKindOfClass:[NSString class]]) {
//        ignoreKeys = @[ignoreKeys];
//    }
//    
//    if (![self umengEnabled]) {
//        [NSException raise:@"接口调用异常" format:@"未开启友盟同步记录事件，请使用不带参数umengIgnoreKeys的同名接口event:attributes:替换此次调用的接口event:attributes:umengIgnoreKeys:"];
//    }else{
//        // 移除友盟无需关注的key-value
//        NSMutableDictionary* umengDict = [NSMutableDictionary dictionaryWithDictionary:attributes];
//        for (NSString* key in ignoreKeys) {
//            [umengDict removeObjectForKey:key];
//        }
//        if (umengDict.count == 0) {
//            [MobClick event:eventCode];
//        }else{
//            [MobClick event:eventCode attributes:umengDict];
//        }
//    }
//    
//    [[MFWClick sharedInstance] event:eventCode.lowercaseString attributes:attributes];
//}



#pragma mark ------ 内部实现 ------

+ (NSString *)GetUUID
{
    CFUUIDRef theUUID = CFUUIDCreate(NULL);
    CFStringRef string = CFUUIDCreateString(NULL, theUUID);
    CFRelease(theUUID);
    
    NSString* guid = [NSString stringWithString:(__bridge NSString *)(string)];
    CFRelease(string);
    
    guid = [guid stringByReplacingOccurrencesOfString:@"-" withString:@""];
    return guid;
}


#pragma 设置初始参数

-(void)sendLocalEvents
{
    // 取本地留存的事件
    NSArray* localEventDictArray = [NSArray arrayWithArray:[self localEventDictArray]];
    if(localEventDictArray.count > 0)
    {
        MSLog(@"发送本地留存的事件");
        // 将之前未发送出去的事件重新发送
        [self sendEventDictArrayInBackground:localEventDictArray completion:^(BOOL success, NSError *error) {
            if(!success)
            {
                MSLog(@"本地留存的事件发送失败");
                // 发送失败也从本地移除，以后不再发送
                [self removeEventDictArrayFromLocal:localEventDictArray];
            }
            else
            {
                MSLog(@"本地留存的事件发送成功");
            }
        }];
    }
    else
    {
        MSLog(@"无本地留存的事件，无需发送");
    }
}
// channelCode 为nil或@""时,被当作@"App Store"渠道
- (void)startWithReportPolicy:(MFWClickReportPolicy)rp channelCode:(NSString*)channelCode
{
//    self.reportPolicy = rp;
//    self.channel_code = channelCode;
//    
//    // 发送本地事件
//    [self sendLocalEvents];
//    
//    // 设置本次启动时的GUID，用于区别事件日志属于哪一次启动
//    self.launch_guid = [MFWClick GetUUID];
//    self.launch_time = [NSDate date];
//    
//    // 发送设备事件（如果设备信息未变更，则不会发送）
//    [self eventDeviceInfoIfChanged];
//    
//    // 检测App安装情况
//    [self checkAppInstall];
//    
//    // 初始化蚂蜂窝Cookie
//    [self initMFWCookie];
}

#pragma mark ------ 设置用户id
- (void)setUserId:(NSString*)uid
{
    if (!uid) {
        uid = @"";
    }
    
    _uid = uid;
    
    [[NSUserDefaults standardUserDefaults] setObject:uid forKey:MFWClick_NSUserDefaults_key_user_id];
    [[NSUserDefaults standardUserDefaults] synchronize];
    
}

- (NSString*)userId
{
    if (!_uid) {
        _uid = [[NSUserDefaults standardUserDefaults] objectForKey:MFWClick_NSUserDefaults_key_user_id];
    }
    
    if (!_uid) {
        _uid = @"";
    }
    
    return _uid;
}



#pragma mark 特殊事件记录

- (void)saveSentDeviceInfoAttrDict:(NSDictionary*)deviceInfoAttrDict
{
    [[NSUserDefaults standardUserDefaults] setObject:deviceInfoAttrDict forKey:MFWClick_NSUserDefaults_key_device_info];
    [[NSUserDefaults standardUserDefaults] synchronize];
}

- (void)saveSentDeviceToken:(NSString*)deviceToken
{
    [[NSUserDefaults standardUserDefaults] setObject:deviceToken forKey:MFWClick_NSUserDefaults_key_device_token];
    [[NSUserDefaults standardUserDefaults] synchronize];
}


// 保存到本地，留待下次发送，要防止重复添加
- (void)addEventDictArray2Local:(NSArray*)eventDictArray
{
    @synchronized(self.arraySendingEventDict){
        // 暂时放到NSUserDefaults里
        NSMutableArray* localArray = (NSMutableArray*)self.arraySendingEventDict;
        NSMutableArray* need2AddArray = [NSMutableArray arrayWithArray:eventDictArray];
        
        for (NSDictionary* localEventDict in localArray) {
            NSDictionary* localBasicDict = [localEventDict objectForKey:MFWClick_event_dict_key_basic];
            NSString* localEventGuid = [localBasicDict objectForKey:MFWClick_basic_key_event_guid];
            for (NSDictionary* eventDict in eventDictArray) {
                NSDictionary* basicDict = [eventDict objectForKey:MFWClick_event_dict_key_basic];
                NSString* eventGuid = [basicDict objectForKey:MFWClick_basic_key_event_guid];
                
                if([eventGuid isEqualToString:localEventGuid])
                {
                    // 本地已存在，不需要再添加
                    [need2AddArray removeObject:eventDict];
                }
            }
        }
        if(need2AddArray.count > 0)
        {
            [localArray addObjectsFromArray:need2AddArray];
            
            // 更新到本地
            [[NSUserDefaults standardUserDefaults] setObject:localArray forKey:MFWClick_NSUserDefaults_key_event_dict_array];
            [[NSUserDefaults standardUserDefaults] synchronize];
        }
    }
}

- (void)removeEventDictArrayFromLocal:(NSArray*)eventDictArray
{
    @synchronized(self.arraySendingEventDict){
        // 暂时放到NSUserDefaults里
        NSMutableArray* localArray = (NSMutableArray*)self.arraySendingEventDict;
        NSMutableArray* need2RemoveArray = [NSMutableArray arrayWithCapacity:0];
        
        for (NSDictionary* localEventDict in localArray) {
            NSDictionary* localBasicDict = [localEventDict objectForKey:MFWClick_event_dict_key_basic];
            NSString* localEventGuid = [localBasicDict objectForKey:MFWClick_basic_key_event_guid];
            for (NSDictionary* eventDict in eventDictArray) {
                NSDictionary* basicDict = [eventDict objectForKey:MFWClick_event_dict_key_basic];
                NSString* eventGuid = [basicDict objectForKey:MFWClick_basic_key_event_guid];
                
                if([eventGuid isEqualToString:localEventGuid])
                {
                    // 本地已存在，准备删除
                    [need2RemoveArray addObject:eventDict];
                }
            }
        }
        
        [need2RemoveArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
            [localArray removeObject:obj];
        }];
        
        // 更新到本地
        [[NSUserDefaults standardUserDefaults] setObject:localArray forKey:MFWClick_NSUserDefaults_key_event_dict_array];
        [[NSUserDefaults standardUserDefaults] synchronize];
    }
}

// 获取保存在本地，未发送的事件
- (void)clearLocalEventDictArray
{
    self.arraySendingEventDict = nil;
    [[NSUserDefaults standardUserDefaults] removeObjectForKey:MFWClick_NSUserDefaults_key_event_dict_array];
    [[NSUserDefaults standardUserDefaults] synchronize];
}

// 未发送设备信息或已发送的设备信息发生变化时，重新发送
- (void)eventDeviceInfoIfChanged
{
//    NSMutableDictionary* attrDict = [NSMutableDictionary dictionaryWithDictionary:@{
//    MFWClick_key_time_zone              :[self time_zone]
//    ,MFWClick_key_country_code           :[self country_code]
//    ,MFWClick_key_language_code          :[self language_code]
//    ,MFWClick_key_sys_ver                :[self sys_ver]
//    ,MFWClick_key_device_model           :[self device_model]
////    ,MFWClick_key_device_id              :[self device_id]
//    ,MFWClick_key_mac:[self mac]
//    }];
//    
//    
//    
//    NSDictionary* alreadySent = [[NSUserDefaults standardUserDefaults] objectForKey:MFWClick_NSUserDefaults_key_device_info];
//    
//    BOOL isEqual = YES;
//    for (NSString* key in alreadySent.allKeys) {
//        if ([key isEqualToString:MFWClick_key_first]) {
//            continue;
//        }
//        if (![[alreadySent objectForKey:key] isEqualToString:[attrDict objectForKey:key]]) {
//            isEqual = NO;
//            break;
//        }
//    }
//    
//    if (!alreadySent) {
//        MSLog(@"第一次发送设备信息");
//        [attrDict setObject:@"1" forKey:MFWClick_key_first];
//        [self event:MFWClick_event_code_sys_device attributes:attrDict];
//    }else if(isEqual){
//        MSLog(@"设备信息未发生变化，无需发送");
//    }else{
//        MSLog(@"设备信息变更，重新发送");
//        [attrDict setObject:@"0" forKey:MFWClick_key_first];
//        [self event:MFWClick_event_code_sys_device attributes:attrDict];
//    }
    
    // 注意，这里不需要设置NSUserDefaults，因为会在后面发送成功后，自动保存到NSUserDefaults中
}

// 记录设备的deviceToken(之前发送过，则不再发送)
- (void)eventDeviceTokenIfChanged:(NSData *)deviceTokenData
{
    if (deviceTokenData.length == 0)
    {
        MSLog(@"deviceTokenData为nil，无法发送");
        return;
    }
    
    NSString *deviceToken = [NSString stringWithFormat:@"%@", deviceTokenData];
    deviceToken = [deviceToken stringByReplacingOccurrencesOfString: @"<" withString: @""];
    deviceToken = [deviceToken stringByReplacingOccurrencesOfString: @">" withString: @""];
    deviceToken = [deviceToken stringByReplacingOccurrencesOfString: @" " withString: @""];
    

    
    NSString* alreadySent = [[NSUserDefaults standardUserDefaults] objectForKey:MFWClick_NSUserDefaults_key_device_token];
    if([deviceToken isEqualToString:alreadySent])
    {
        MSLog(@"deviceToken未发生变化，无需再发送");
        return;
    }
    else if(alreadySent.length == 0)
    {
        MSLog(@"deviceToken首次发送");
        [self event:MFWClick_event_code_sys_device_token attributes:@{MFWClick_key_device_token : deviceToken,MFWClick_key_first:@"1"}];
    }
    else
    {
        MSLog(@"deviceToken发生变化");
        [self event:MFWClick_event_code_sys_device_token attributes:@{MFWClick_key_device_token : deviceToken,MFWClick_key_first:@"0"}];
    }
    
    // 注意，这里不需要设置NSUserDefaults，因为会在后面发送成功后，自动保存到NSUserDefaults中
}
//// 记录设备的经纬度坐标
//- (void)eventGPSWithLatitude:(double)latitude longitude:(double)longitude
//{
//    NSString* lat = [NSString stringWithFormat:@"%f",latitude];
//    NSString* lng = [NSString stringWithFormat:@"%f",longitude];
//
//    [self event:MFWClick_event_code_sys_gps attributes:@{MFWClick_key_latitude:lat,MFWClick_key_longitude:lng}];
//    
//    self.alreadySentGPS = YES;
//}

-(void)eventGPSWithLocation:(CLLocation *)newLocation
{
    NSString* lat = [NSString stringWithFormat:@"%f",newLocation.coordinate.latitude];
    NSString* lng = [NSString stringWithFormat:@"%f",newLocation.coordinate.longitude];
    NSString* altitude = [NSString stringWithFormat:@"%f",newLocation.altitude];
    NSString* horizontalAccuracy = [NSString stringWithFormat:@"%f",newLocation.horizontalAccuracy];
    NSString* verticalAccuracy = [NSString stringWithFormat:@"%f",newLocation.verticalAccuracy];
    NSString* course = [NSString stringWithFormat:@"%f",newLocation.course];
    NSString* speed = [NSString stringWithFormat:@"%f",newLocation.speed];

    [self event:MFWClick_event_code_sys_gps attributes:@{MFWClick_key_latitude:lat
     ,MFWClick_key_longitude:lng
     ,MFWClick_key_altitude:altitude
     ,MFWClick_key_horizontal_accuracy:horizontalAccuracy
     ,MFWClick_key_vertical_accuracy:verticalAccuracy
     ,MFWClick_key_course:course
     ,MFWClick_key_speed:speed
     }];

    self.alreadySentGPS = YES;
}

#pragma mark 通用事件记录
- (void)beginLogPageViewWithTrigger:(MFWClickTrigger*)trigger pageName:(NSString*)pageName
{
    if(self.arrayPageViewing == nil)
    {
        self.arrayPageViewing  = [[NSMutableArray alloc]init];
    }

 
    // 记录下此页面的访问起始时间
    NSMutableDictionary* pageStartDict = [NSMutableDictionary dictionaryWithDictionary:@{PageViewing_key_page_name:pageName,PageViewing_key_start_time:[NSDate date]}];
    if (trigger) {
        pageStartDict[PageViewing_key_trigger] = trigger;
    }
    
    if (pageName) {
        [self.arrayPageViewing insertObject:pageStartDict atIndex:0];   // 始终保持最新页面在开头
    }

}

//- (void)endLogPageView:(NSString*)pageName
//{
//    [self endLogPageViewWithTrigger:nil pageName:pageName];
//}
//
//- (void)endLogPageViewWithTrigger:(MFWClickTrigger*)trigger pageName:(NSString*)pageName
//{
//
//}



- (void)event:(NSString *)eventCode attributes:(NSDictionary *)attributes
{
    // 准备发送到蚂蜂窝事件服务
    NSDictionary* eventDict = [self packEventDictWithEventCode:eventCode attributes:attributes];
    [self ready2SendEventDict:eventDict];
}


#pragma mark ------ 基础信息 ------


#pragma mark 本机动态信息

// 渠道代码
-(NSString*) channel_code
{
    if (_channel_code.length == 0
        || [_channel_code.lowercaseString isEqualToString:@"Apple Store".lowercaseString]
        || [_channel_code.lowercaseString isEqualToString:@"App Store".lowercaseString]) {
        return @"";
    }else{
        return _channel_code;
    }
}

// 暂时不用
-(NSString *)getUTCFormateDate:(NSDate *)localDate
{
    NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
    NSTimeZone *timeZone = [NSTimeZone timeZoneWithName:@"UTC"];
    [dateFormatter setTimeZone:timeZone];
    [dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
    NSString *dateString = [dateFormatter stringFromDate:localDate];
    return dateString;
}


#pragma mark 本机固定信息

// 时区
- (NSString*)time_zone
{
    @try {
        NSString* returnStr = [NSTimeZone localTimeZone].description;
        if (returnStr.length == 0) {
            [NSException raise:@"获取基本信息异常" format:@"未取到或空字符串"];
        }
        return returnStr;
    }
    @catch (NSException *exception) {
#ifdef DEBUG
        MSLog(@"[MFWClick][异常]未能取到time_zone:%@",exception);
        [exception raise];
#endif
        return @"error";
    }
}

// 国家代码
- (NSString *)country_code
{
    @try {
        NSString* returnStr = [[NSLocale currentLocale] objectForKey:NSLocaleCountryCode];
        if (returnStr.length == 0) {
            [NSException raise:@"获取基本信息异常" format:@"未取到或空字符串"];
        }
        return returnStr;
    }
    @catch (NSException *exception) {
#ifdef DEBUG
        MSLog(@"[MFWClick][异常]未能取到country_code:%@",exception);
        [exception raise];
#endif
        return @"error";
    }

}
// 语言代码
- (NSString *)language_code
{
    @try {
        NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
        NSArray *languages = [defaults objectForKey:@"AppleLanguages"];
        
        NSString* returnStr = [languages objectAtIndex:0];
        if (returnStr.length == 0) {
            [NSException raise:@"获取基本信息异常" format:@"未取到或空字符串"];
        }
        return returnStr;
    }
    @catch (NSException *exception) {
#ifdef DEBUG
        MSLog(@"[MFWClick][异常]未能取到language_code:%@",exception);
        [exception raise];
#endif
        return @"error";
    }
}

// 应用代码
-(NSString*) app_code
{
    @try {
        NSString* returnStr = [[NSBundle mainBundle] bundleIdentifier];
        if (returnStr.length == 0) {
            [NSException raise:@"获取基本信息异常" format:@"未取到或空字符串"];
        }
        return returnStr;
    }
    @catch (NSException *exception) {
#ifdef DEBUG
        MSLog(@"[MFWClick][异常]未能取到app_code:%@",exception);
        [exception raise];
#endif
        return @"error";
    }
}
// 应用版本号
-(NSString*) app_ver
{
    @try {
        NSString* returnStr = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"];
        if (returnStr.length == 0) {
            [NSException raise:@"获取基本信息异常" format:@"未取到或空字符串"];
        }
        return returnStr;
    }
    @catch (NSException *exception) {
#ifdef DEBUG
        MSLog(@"[MFWClick][异常]未能取到app_ver:%@",exception);
        [exception raise];
#endif
        return @"error";
    }
}
// 系统版本号
-(NSString*) sys_ver
{
    @try {
        NSString* returnStr = [[UIDevice currentDevice] systemVersion];
        if (returnStr.length == 0) {
            [NSException raise:@"获取基本信息异常" format:@"未取到或空字符串"];
        }
        return returnStr;
    }
    @catch (NSException *exception) {
#ifdef DEBUG
        MSLog(@"[MFWClick][异常]未能取到app_ver:%@",exception);
        [exception raise];
#endif
        return @"error";
    }
}
// 设备型号
-(NSString*) device_model
{
    @try {
        NSString* returnStr = [self machine];
        if (returnStr.length == 0) {
            [NSException raise:@"获取基本信息异常" format:@"未取到或空字符串"];
        }
        return returnStr;
    }
    @catch (NSException *exception) {
#ifdef DEBUG
        MSLog(@"[MFWClick][异常]未能取到device_model:%@",exception);
        [exception raise];
#endif
        return @"error";
    }
}
//// 设备名称
//-(NSString*)device_name
//{
//    return [[UIDevice currentDevice] name];
//}
// 设备id(将来会被废弃，以open_udid为准)
//-(NSString*)device_id
//{
//    @try {
//        NSString* returnStr = [[UIDevice currentDevice] uniqueIdentifier];
//        if (returnStr.length == 0) {
//            [NSException raise:@"获取基本信息异常" format:@"未取到或空字符串"];
//        }
//        return returnStr;
//    }
//    @catch (NSException *exception) {
//#ifdef DEBUG
//        MSLog(@"[MFWClick][异常]未能取到device_id:%@",exception);
//        [exception raise];
//#endif
//        return @"error";
//    }
//}
-(NSString*)idfv
{
    @try {
        if ([[UIDevice currentDevice] respondsToSelector:@selector(identifierForVendor)]) {
            NSString* returnStr = [[UIDevice currentDevice] identifierForVendor].UUIDString;
            if (returnStr.length == 0) {
                [NSException raise:@"获取基本信息异常" format:@"未取到或空字符串"];
            }
            return returnStr;
        }else{
            return @"";
        }
    }
    @catch (NSException *exception) {
#ifdef DEBUG
        MSLog(@"[MFWClick][异常]未能取到identifier_for_vendor:%@",exception);
        [exception raise];
#endif
        return @"error";
    }

}

// 只要用户不关闭，同一设备的所有app共享相同的值 only available on iOS6+

-(NSString*)open_udid
{
    @try {
        NSString* returnStr = [OpenUDID value];
        if (returnStr.length == 0) {
            [NSException raise:@"获取基本信息异常" format:@"未取到或空字符串"];
        }
        return returnStr;
    }
    @catch (NSException *exception) {
#ifdef DEBUG
        MSLog(@"[MFWClick][异常]未能取到open_udid:%@",exception);
        [exception raise];
#endif
        return @"error";
    }
}

-(NSString*)device_token
{
    NSString* ret = [[NSUserDefaults standardUserDefaults] objectForKey:MFWClick_NSUserDefaults_key_device_token];
    if(ret){
        return ret;
    }else{
        return @"";
    }
}


- (NSString *)machine {
    /*
     @"i386"      on the simulator
     @"iPod1,1"   on iPod Touch
     @"iPod2,1"   on iPod Touch Second Generation
     @"iPod3,1"   on iPod Touch Third Generation
     @"iPod4,1"   on iPod Touch Fourth Generation
     @"iPhone1,1" on iPhone
     @"iPhone1,2" on iPhone 3G
     @"iPhone2,1" on iPhone 3GS
     @"iPad1,1"   on iPad
     @"iPad2,1"   on iPad 2
     @"iPhone3,1" on iPhone 4
     @"iPhone4,1" on iPhone 4S
     */
    
    size_t size;
    
    // Set 'oldp' parameter to NULL to get the size of the data
    // returned so we can allocate appropriate amount of space
    sysctlbyname("hw.machine", NULL, &size, NULL, 0);
    
    // Allocate the space to store name
    char *name = malloc(size);
    
    // Get the platform name
    sysctlbyname("hw.machine", name, &size, NULL, 0);
    
    // Place name into a string
    NSString *machine;
    
    machine = [NSString stringWithCString:name encoding:[NSString defaultCStringEncoding]];
    
    
    // Done with this
    free(name);
    
    return machine;
}

- (NSString * )mac
{
    int                 mib[6];
    size_t              len;
    char                *buf;
    unsigned char       *ptr;
    struct if_msghdr    *ifm;
    struct sockaddr_dl  *sdl;
    
    mib[0] = CTL_NET;
    mib[1] = AF_ROUTE;
    mib[2] = 0;
    mib[3] = AF_LINK;
    mib[4] = NET_RT_IFLIST;
    
    if ((mib[5] = if_nametoindex("en0")) == 0) {
        printf("Error: if_nametoindex error\n");
        return NULL;
    }
    
    if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) {
        printf("Error: sysctl, take 1\n");
        return NULL;
    }
    
    if ((buf = malloc(len)) == NULL) {
        printf("Could not allocate memory. error!\n");
        return NULL;
    }
    
    if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) {
        printf("Error: sysctl, take 2");
        free(buf);
        return NULL;
    }
    
    ifm = (struct if_msghdr *)buf;
    sdl = (struct sockaddr_dl *)(ifm + 1);
    ptr = (unsigned char *)LLADDR(sdl);
    NSString *macString = [NSString stringWithFormat:@"%02X:%02X:%02X:%02X:%02X:%02X",
                           *ptr, *(ptr+1), *(ptr+2), *(ptr+3), *(ptr+4), *(ptr+5)];
    free(buf);
    
    return macString;
}


#pragma mark GPS方位

-(void)startUpdatingGPS
{
    self.alreadySentGPS = NO;
    
    switch([CLLocationManager authorizationStatus])
    {
        case kCLAuthorizationStatusNotDetermined:
            if ([MFWClick umengEnabled]) {
//                [MobClick event:MFWClick_event_code_sys_gps label:@"待定"];
            }
            break;
        case kCLAuthorizationStatusRestricted:
            if ([self umengEnabled]) {
//                [MobClick event:MFWClick_event_code_sys_gps label:@"受限"];
            }
            break;
        case kCLAuthorizationStatusAuthorized:
            // 放到delegate对应方法里记录
            break;
        case kCLAuthorizationStatusDenied:
            if ([self umengEnabled]) {
//                [MobClick event:MFWClick_event_code_sys_gps label:@"禁止"];
            }
            break;
        default:
            break;
    }
    
    if ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusAuthorized
        && [CLLocationManager locationServicesEnabled]
        && [CLLocationManager significantLocationChangeMonitoringAvailable])
    {
        self.locationManager = [[CLLocationManager alloc]init];
        self.locationManager.delegate = self;
        [self.locationManager startMonitoringSignificantLocationChanges];
    }
}

-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
    if (self.alreadySentGPS) {
        return; // 已经发送过，不再重复发送
    }
    
    self.alreadySentGPS = YES;
    
    if ([self umengEnabled]) {
//        [MobClick event:MFWClick_event_code_sys_gps label:@"定位成功"];
    }
    
    CLLocation* location = [locations lastObject];
    
    // 发送给友盟
//    [MobClick setLocation:location];
    
    // 发送给MFWClick
//    [self eventGPSWithLocation:location];

    
    [manager stopMonitoringSignificantLocationChanges];
}
-(void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
    self.alreadySentGPS = YES;
    
    if ([self umengEnabled]) {
//        [MobClick event:MFWClick_event_code_sys_gps label:@"定位失败"];
    }
    
    [manager stopMonitoringSignificantLocationChanges];
}

#pragma mark 竞品分析
-(NSDictionary*)dictAppURLSchemes
{
    NSDictionary* dict = @{@"mfw":@[
                                   @"travelguide"
                                    ,@"travelguideipad"
                                    ,@"mfwios"
                                    ,@"wengweng"
                                    ,@"voiceguide"
                                    ,@"voiceguideall"
                                    ,@"mfwhotel"
                                    ,@"youji"
                                    ,@"oauthmfwtn"
                                   ]
                           ,@"competitor":@[
                                   @"qyer"
                                    ,@"wx10bac023d466fe05"
                                    ,@"wx68fc1a4ab955c518"
                                    ,@"ontheroad"
                                    ,@"chanyoujitrips"
                                    ,@"breadtrip"
                                    ,@"ctrip"
                                    ,@"QunariPhone"
                                    ,@"com.qunar.gonglue"
                                    ,@"QunarHotelAliPay"
                                    ,@"TUNIUIPHONE"
                                    ,@"webappbaidulvyou123"
                                    ,@"elongIPhone"
                                    ,@"Lvmama"
                                    ,@"tctravel"
                                    ,@"taobaotravel"
                                    ,@"wx268e5fa0f60d3eb3"
                                    ,@"tripadvisor"
                                   ]
                           ,@"else":@[
                                   @"dianping"
                                    ,@"baidumap"
                                    ,@"iosamap"
                                    ,@"googlemaps"

                                   ]
                           };
    return dict;    // todo for hanxin 改为从网络获取
}

-(void)checkAppInstall
{
    NSDictionary* dict = [self dictAppURLSchemes];
    NSMutableDictionary* resultDict = [NSMutableDictionary dictionary];
    NSMutableDictionary* publicDict = [NSMutableDictionary dictionary];
    NSMutableDictionary* privateDict = [NSMutableDictionary dictionary];
    
    [dict enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
        NSArray* array = obj;
        NSMutableArray* resultInstalled = [NSMutableArray array];
        NSMutableArray* resultNotInstalled = [NSMutableArray array];
        
        [array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
            NSURL* url = [NSURL URLWithString:[NSString stringWithFormat:@"%@://",obj]];
            
            if([[UIApplication sharedApplication] canOpenURL:url]){
                MSLog(@"MFWClick:[%@][installed]",obj);
                [resultInstalled addObject:obj];
            }else{
                MSLog(@"MFWClick:[%@][not installed]",obj);
                [resultNotInstalled addObject:obj];
            }
        }];
        
        if (resultInstalled.count > 0) {
            resultDict[[NSString stringWithFormat:@"%@_installed",key]] = resultInstalled;
        }
        if (resultNotInstalled.count > 0) {
            resultDict[[NSString stringWithFormat:@"%@_uninstalled",key]] = resultNotInstalled;
        }
        
        publicDict[[NSString stringWithFormat:@"num_%@",key]] = [NSString stringWithFormat:@"%d",[resultInstalled count]];
    }];
    
    

    
    [resultDict enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
        NSArray* array = obj;
        privateDict[key] = [array componentsJoinedByString:@","];
        MSLog(@"MFWClick:ResultDict:%@:%d",key,[obj count]);
    }];
    
    [MFWClick event:MFWClick_event_code_app_install publicAttributesBlock:^NSDictionary *{
        return publicDict;
    } privateAttributesBlock:^NSDictionary *{
        return privateDict;
    }];
}

#pragma mark 与网站日志打通

+ (NSString *)mfwUUID
{
    return [MFWClick sharedInstance].mfwUUID;
}

-(NSString*)mfwUUID
{
    NSString* str = [[NSUserDefaults standardUserDefaults] objectForKey:MFWClick_NSUserDefaults_key_mfwUUID];
    if (!str) {
        str = @"";
    }
    return str;
}

-(void)setMfwUUID:(NSString*)mfwUUID
{
    [[NSUserDefaults standardUserDefaults] setObject:mfwUUID forKey:MFWClick_NSUserDefaults_key_mfwUUID];
}



-(void)initMFWCookie
{
//    NSURLRequest*request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://tongji.mafengwo.cn/m_click.php"]
//                                            cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData
//                                        timeoutInterval:3];
//    
//    [NSURLConnection sendSynchronousRequest:request
//                          returningResponse:nil
//                                      error:nil];
//    
//    [NSURLConnection sendAsynchronousRequest:request queue:[[NSOperationQueue alloc] init] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
//        
//        
//        // 更新最新的网站uuid
//        NSArray* cookies = [NSHTTPCookieStorage sharedHTTPCookieStorage].cookies;
//        [cookies enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
//            NSHTTPCookie* cookie = obj;
//            if ([cookie.domain isEqualToString:@".mafengwo.cn"]) {
//                if ([cookie.name isEqualToString:@"mfw_uuid"]) {
//                    [self setMfwUUID:cookie.value];
//                }
//            }
//        }];
//        
//        
//        // 设置App的openUDID
//        NSDate* expireDate = [[NSDate date] dateByAddingTimeInterval:3600 * 24 * 365];
//        NSMutableDictionary *cookieProperties = [NSMutableDictionary dictionaryWithObjectsAndKeys:
//                                                 @"__openudid", NSHTTPCookieName,
//                                                 [self open_udid], NSHTTPCookieValue,
////                                                 @"www.mafengwo.cn", NSHTTPCookieOriginURL,
//                                                 @".mafengwo.cn", NSHTTPCookieDomain,
//                                                 @"/", NSHTTPCookiePath,
//                                                 @"0", NSHTTPCookieVersion,
//                                                 expireDate, NSHTTPCookieExpires,
//                                                 nil];
//        NSHTTPCookie* cookieOpenUDID = [NSHTTPCookie cookieWithProperties:cookieProperties];
//        [[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:cookieOpenUDID];
//    }];
    

}

@end
